package ink.mint.matrix;

import java.io.Serializable;
import java.math.BigDecimal;

public class Matrix implements Matrixable, Serializable {

    /**
     * 矩阵行数
     */
    private int row = 0;
    /**
     * 矩阵列数
     */
    private int column = 0;

    /**
     * 无参构造函数
     */
    public Matrix() {

    }

    /**
     * 构造函数
     *
     * @param row 向量的行数
     */
    public Matrix(int row) {
        this.row = row;
    }

    /**
     * 构造函数
     *
     * @param row    向量的行数
     * @param column 向量的列数
     */
    public Matrix(int row, int column) {
        this.row = row;
        this.column = column;
    }


    /**
     * 随机向量
     *
     * @param row 向量行
     * @return 一个随机向量
     */
    @Override
    public double[] random(int row) {
        double[] result = new double[row];
        for (int i = 0; i < row; i++) {
            result[i] = Math.random() - 0.5;
        }
        return result;
    }

    /**
     * 随机矩阵
     *
     * @param row    矩阵的行数
     * @param column 矩阵的列数
     * @return 一个随机矩阵
     */
    @Override
    public double[][] random(int row, int column) {
        double[][] result = new double[row][column];
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < column; j++) {
                result[i][j] = Math.random() - 0.5;
            }
        }
        return result;
    }

    /**
     * 零向量
     *
     * @param row 向量的行
     * @return 一个零向量
     */
    @Override
    public double[] zero(int row) {
        double[] result = new double[row];
        return result;
    }

    /**
     * 自定义数的向量（所有元素相同）
     *
     * @param customNumber 自定义数
     * @return 自定义数的向量（所有元素相同）
     */
    @Override
    public double[] customNumber(double customNumber, int row) {
        double[] result = new double[row];
        for (int i = 0; i < row; i++) {
            result[i] = customNumber;
        }
        return result;
    }

    /**
     * 自定义数的矩阵（所有元素相同）
     *
     * @param customNumber 自定义数
     * @return 自定义数的矩阵（所有元素相同）
     */
    @Override
    public double[][] customNumber(double customNumber, int row, int column) {
        double[][] result = new double[row][column];
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < column; j++) {
                result[i][j] = customNumber;
            }
        }
        return result;
    }


    /**
     * 零矩阵
     *
     * @param row    矩阵的行
     * @param column 矩阵的列
     * @return 一个零矩阵
     */
    @Override
    public double[][] zero(int row, int column) {
        double[][] result = new double[row][column];
        return result;
    }

    /**
     * one-hot 编码
     *
     * @param number         数字
     * @param dimensionality 维度
     * @return [0, 0, 0, 1, 0, 0....., 0, 0]
     */
    public double[] oneHotEncoding(int number, int dimensionality) {
        double[] result = new double[dimensionality];
        result[number] = 1.0;
        return result;
    }

    /**
     * 矩阵转置
     *
     * @param A 矩阵A
     * @return 转置矩阵
     */
    @Override
    public double[][] transpose(double[][] A) {
        int ar = A.length;
        int ac = A[0].length;
        double[][] result = new double[ac][ar];
        for (int i = 0; i < ac; i++) {
            for (int j = 0; j < ar; j++) {
                result[i][j] = A[j][i];
            }
        }
        return result;
    }

    /**
     * 列向量转变一行矩阵
     *
     * @param A 向量
     * @return 单行矩阵{{1,2,3...}}
     */
    @Override
    public double[][] vectorToRowMatrix(double[] A) {
        int ar = A.length;
        double[][] result = new double[1][ar];
        for (int i = 0; i < ar; i++) {
            result[0][i] = A[i];
        }
        return result;
    }

    /**
     * 一个数拓展为一个向量
     *
     * @param number 一个数
     * @param ar     向量维度
     * @return 一个相同数值的向量
     */
    public double[] numberCopyToVector(double number, int ar) {
        double[] result = new double[ar];
        for (int i = 0; i < ar; i++) {
            result[i] = number;
        }
        return result;
    }

    /**
     * 列向量转变一行多列矩阵
     *
     * @param A 向量
     * @return 单行多列矩阵{{1},{2},{3}...}
     */
    @Override
    public double[][] vectorToColumnMatrix(double[] A) {
        int ar = A.length;
        double[][] result = new double[ar][1];
        for (int i = 0; i < ar; i++) {
            result[i][0] = A[i];
        }
        return result;
    }

    /**
     * 一行矩阵变向量
     *
     * @param A 单行矩阵
     * @return 向量
     */
    @Override
    public double[] matrixToVector(double[][] A) {
        int ar = A.length;
        int ac = A[0].length;
        double[] result;
        if (ar == 1) {
            result = new double[ac];
            for (int i = 0; i < ar; i++) {
                for (int j = 0; j < ac; j++) {
                    result[j] = A[i][j];
                }
            }
        } else if (ac == 1) {
            result = new double[ar];
            for (int i = 0; i < ar; i++) {
                for (int j = 0; j < ac; j++) {
                    result[i] = A[i][j];
                }
            }
        } else {
            System.out.println("矩阵A 没有一维的行或列，不适合将矩阵转换为向量。");
            return null;
        }
        return result;
    }

    /**
     * 向量最大值索引
     *
     * @param A 向量
     * @return 向量的最大值的索引
     */
    @Override
    public int maxVectorIndex(double[] A) {
        int ar = A.length;
        int maxIndex = -1;
        double max = 0;
        for (int i = 0; i < ar; i++) {
            if (A[i] > max) {
                max = A[i];
                maxIndex = i;
            } else if (A[i] < max) {
                max = max;
                maxIndex = maxIndex;
            }
        }
        return maxIndex;
    }

    /**
     * 向量取反
     *
     * @param A 向量A
     * @return 反向量
     */
    @Override
    public double[] not(double[] A) {
        int ar = A.length;
        double[] result = new double[ar];
        for (int i = 0; i < ar; i++) {
            BigDecimal bigDecimal = new BigDecimal(Double.toString(A[i]));
            result[i] = bigDecimal.negate().doubleValue();
        }
        return result;
    }

    /**
     * 向量绝对值
     *
     * @param A 向量A
     * @return 绝对值向量
     */
    @Override
    public double[] abs(double[] A) {
        int ar = A.length;
        double[] result = new double[ar];
        for (int i = 0; i < ar; i++) {
            result[i] = Math.abs(A[i]);
        }
        return result;
    }

    /**
     * 矩阵绝对值
     *
     * @param A 矩阵A
     * @return 绝对值矩阵
     */
    @Override
    public double[][] abs(double[][] A) {
        int ar = A.length;
        int ac = A[0].length;
        double[][] result = new double[ar][ac];
        for (int i = 0; i < ar; i++) {
            for (int j = 0; j < ac; j++) {
                result[i][j] = Math.abs(A[i][j]);
            }
        }
        return result;
    }

    /**
     * 向量总和
     *
     * @param A 向量A
     * @return 向量的总和  一个标量
     */
    @Override
    public double vectorSum(double[] A) {
        double result = 0.0;
        int ar = A.length;
        for (int i = 0; i < ar; i++) {
            result += A[i];
        }
        return result;
    }

    /**
     * log 自定义向量
     *
     * @param A    向量A
     * @param base log的底数
     * @return 以base为底的log向量
     */
    public double[] log(double[] A, double base) {
        int ar = A.length;
        double[] result = new double[ar];
        for (int i = 0; i < ar; i++) {
            result[i] = Math.log10(A[i]) / Math.log10(base);
        }
        return result;
    }

    /**
     * log 自定义矩阵
     *
     * @param A    矩阵A
     * @param base log的底数
     * @return 以base为底的log矩阵
     */
    public double[][] log(double[][] A, double base) {
        int ar = A.length;
        int ac = A[0].length;
        double[][] result = new double[ar][ac];
        for (int i = 0; i < ar; i++) {
            for (int j = 0; j < ac; j++) {
                result[i][j] = Math.log10(A[i][j]) / Math.log10(base);
            }
        }
        return result;
    }

    /**
     * ln向量
     *
     * @param A 向量A
     * @return ln向量 以e为底的自然对数
     */
    @Override
    public double[] ln(double[] A) {
        int ar = A.length;
        double[] result = new double[ar];
        for (int i = 0; i < ar; i++) {
            if (A[i] == 0 || Double.isNaN(A[i])) {
                System.out.println("ln向量A不能取值为0！" + A[i]);
                return null;
            } else {
                result[i] = Math.log(A[i]);
            }
        }
        return result;
    }

    /**
     * ln矩阵
     *
     * @param A 矩阵A
     * @return ln矩阵 以e为底的自然对数
     */
    @Override
    public double[][] ln(double[][] A) {
        int ar = A.length;
        int ac = A[0].length;
        double[][] result = new double[ar][ac];
        for (int i = 0; i < ar; i++) {
            for (int j = 0; j < ac; j++) {
                result[i][j] = Math.log(A[i][j]);
            }
        }
        return result;
    }

    /**
     * 一个数减向量
     *
     * @param number 一个数
     * @param A      向量A
     * @return 被一个数减去的向量
     */
    @Override
    public double[] sub(double number, double[] A) {
        int ar = A.length;
        double[] result = new double[ar];
        for (int i = 0; i < ar; i++) {
            result[i] = number - A[i];
        }
        return result;
    }

    /**
     * 向量减去一个数
     *
     * @param A      向量A
     * @param number 一个数
     * @return 向量减去一个数
     */
    @Override
    public double[] sub(double[] A, double number) {
        int ar = A.length;
        double[] result = new double[ar];
        for (int i = 0; i < ar; i++) {
            result[i] = A[i] - number;
        }
        return result;
    }

    /**
     * 向量除法
     *
     * @param A 向量A
     * @param B 向量B
     * @return 向量被除结果
     */
    @Override
    public double[] division(double[] A, double[] B) {
        int ar = A.length;
        int br = B.length;
        if (ar != br) {
            System.out.println("除法向量A " + ar + " 维 与 向量B " + br + " 维  因维度不同");
            return null;
        }
        double[] result = new double[ar];
        for (int i = 0; i < ar; i++) {
            result[i] = A[i] / B[i];
        }
        return result;
    }

    /**
     * 矩阵除法
     *
     * @param A 矩阵A
     * @param B 矩阵B
     * @return 矩阵各元素被除结果
     */
    @Override
    public double[][] division(double[][] A, double[][] B) {
        int ar = A.length;
        int ac = A[0].length;
        int br = B.length;
        int bc = B[0].length;
        if (ar != br || ac != bc) {
            System.out.println("加法矩阵A " + ar + " 行 " + ac + " 列 与 矩阵B " + br + " 行 " + bc + " 列 因维度不同。");
            return null;
        }
        double[][] result = new double[ar][ac];
        for (int i = 0; i < ar; i++) {
            for (int j = 0; j < ac; j++) {
                result[i][j] = A[i][j] / B[i][j];
            }
        }
        return result;
    }

    /**
     * 向量加法
     *
     * @param A 向量A
     * @param B 向量B
     * @return 向量的和
     */
    @Override
    public double[] add(double[] A, double[] B) {
        int ar = A.length;
        int br = B.length;
        if (ar != br) {
            System.out.println("加法向量A " + ar + " 维 与 向量B " + br + " 维  因维度不同");
            return null;
        }
        double[] result = new double[ar];
        for (int i = 0; i < ar; i++) {
            result[i] = A[i] + B[i];
        }
        return result;
    }

    /**
     * 矩阵加法
     *
     * @param A 矩阵A
     * @param B 矩阵B
     * @return 矩阵的和
     */
    @Override
    public double[][] add(double[][] A, double[][] B) {
        int ar = A.length;
        int ac = A[0].length;
        int br = B.length;
        int bc = B[0].length;
        if (ar != br || ac != bc) {
            System.out.println("加法矩阵A " + ar + " 行 " + ac + " 列 与 矩阵B " + br + " 行 " + bc + " 列 因维度不同。");
            return null;
        }
        double[][] result = new double[ar][ac];
        for (int i = 0; i < ar; i++) {
            for (int j = 0; j < ac; j++) {
                result[i][j] = A[i][j] + B[i][j];
            }
        }
        return result;
    }

    /**
     * 向量减法
     *
     * @param A 向量A
     * @param B 向量B
     * @return 向量的差
     */
    @Override
    public double[] sub(double[] A, double[] B) {
        int ar = A.length;
        int br = B.length;
        if (ar != br) {
            System.out.println("减法向量A " + ar + " 维 与 向量B " + br + " 维  因维度不同");
            return null;
        }
        double[] result = new double[ar];
        for (int i = 0; i < ar; i++) {
            result[i] = A[i] - B[i];
        }
        return result;
    }

    /**
     * 矩阵的减法
     *
     * @param A 矩阵A
     * @param B 矩阵B
     * @return 矩阵的差
     */
    @Override
    public double[][] sub(double[][] A, double[][] B) {
        int ar = A.length;
        int ac = A[0].length;
        int br = B.length;
        int bc = B[0].length;
        if (ar != br || ac != bc) {
            System.out.println("减法矩阵A " + ar + " 行 " + ac + " 列 与 矩阵B " + br + " 行 " + bc + " 列 因维度不同。");
            return null;
        }
        double[][] result = new double[ar][ac];
        for (int i = 0; i < ar; i++) {
            for (int j = 0; j < ac; j++) {
                result[i][j] = A[i][j] - B[i][j];
            }
        }
        return result;
    }

    /**
     * 向量元素点积
     *
     * @param A 向量A
     * @param B 向量B
     * @return 向量元素点积
     */
    @Override
    public double[] elementProduct(double[] A, double[] B) {
        int ar = A.length;
        int br = B.length;
        if (ar != br) {
            System.out.println("元素乘法向量A " + ar + " 维 与 向量B " + br + " 维  因维度不同");
            return null;
        }
        double[] result = new double[ar];
        for (int i = 0; i < ar; i++) {
            result[i] = A[i] * B[i];
        }
        return result;
    }

    /**
     * 矩阵元素点积
     *
     * @param A 矩阵A
     * @param B 矩阵B
     * @return 矩阵元素点积
     */
    @Override
    public double[][] elementProduct(double[][] A, double[][] B) {
        int ar = A.length;
        int ac = A[0].length;
        int br = B.length;
        int bc = B[0].length;
        if (ar != br || ac != bc) {
            System.out.println("元素乘法矩阵A " + ar + " 行 " + ac + " 列 与 矩阵B " + br + " 行 " + bc + " 列 因维度不同。");
            return null;
        }
        double[][] result = new double[ar][ac];
        for (int i = 0; i < ar; i++) {
            for (int j = 0; j < ac; j++) {
                result[i][j] = A[i][j] * B[i][j];
            }
        }
        return result;
    }

    /**
     * 向量的幂
     *
     * @param A      向量A
     * @param number 幂
     * @return 向量的幂
     */
    @Override
    public double[] pow(double[] A, double number) {
        int ar = A.length;
        double[] result = new double[ar];
        for (int i = 0; i < ar; i++) {
            result[i] = Math.pow(A[i], number);
        }
        return result;
    }

    /**
     * 向量乘以一个数
     *
     * @param A      向量A
     * @param number 一个数
     * @return 向量乘以一个数
     */
    @Override
    public double[] multi(double[] A, double number) {
        int ar = A.length;
        double[] result = new double[ar];
        for (int i = 0; i < ar; i++) {
            result[i] = A[i] * number;
        }
        return result;
    }

    /**
     * 向量求和
     *
     * @param A 向量A
     * @return 向量的值的总和
     */
    @Override
    public double sum(double[] A) {
        int ar = A.length;
        double result = 0;
        for (int i = 0; i < ar; i++) {
            result += A[i];
        }
        return result;
    }

    /**
     * 矩阵求和
     *
     * @param A 矩阵A
     * @return 矩阵的值的总和
     */
    @Override
    public double sum(double[][] A) {
        int ar = A.length;
        int ac = A[0].length;
        double result = 0;
        for (int i = 0; i < ar; i++) {
            for (int j = 0; j < ac; j++) {
                result += A[i][j];
            }
        }
        return result;
    }

    /**
     * 将矩阵所有元素加和位标量
     *
     * @param A 矩阵A
     * @return 所求的矩阵标量
     */
    public double matrixSum(double[][] A) {
        int ar = A.length;
        int ac = A[0].length;
        double result = 0;
        for (int i = 0; i < ar; i++) {
            for (int j = 0; j < ac; j++) {
                result += A[i][j];
            }
        }
        return result;
    }

    /**
     * 矩阵压缩为一维向量
     *
     * @param A 矩阵A
     * @return 矩阵A压缩后的一维向量
     */
    @Override
    public double[] matrixSumToVector(double[][] A) {
        int ar = A.length;
        int ac = A[0].length;
        double[] result = new double[ar];
        for (int i = 0; i < ar; i++) {
            for (int j = 0; j < ac; j++) {
                result[i] += A[i][j];
            }
        }
        return result;
    }

    /**
     * 矩阵的幂
     *
     * @param A      矩阵A
     * @param number 幂
     * @return 矩阵的幂
     */
    @Override
    public double[][] pow(double[][] A, double number) {
        int ar = A.length;
        int ac = A[0].length;
        double[][] result = new double[ar][ac];
        for (int i = 0; i < ar; i++) {
            for (int j = 0; j < ac; j++) {
                result[i][j] = Math.pow(A[i][j], number);
            }
        }
        return result;
    }

    /**
     * 矩阵乘以一个数
     *
     * @param A      矩阵A
     * @param number 一个数
     * @return 矩阵乘以一个数
     */
    @Override
    public double[][] multi(double[][] A, double number) {
        int ar = A.length;
        int ac = A[0].length;
        double[][] result = new double[ar][ac];
        for (int i = 0; i < ar; i++) {
            for (int j = 0; j < ac; j++) {
                result[i][j] = A[i][j] * number;
            }
        }
        return result;
    }

    /**
     * 矩阵的乘法
     *
     * @param A 矩阵A
     * @param B 矩阵B
     * @return 两个矩阵的积
     */
    @Override
    public double[][] multi(double[][] A, double[][] B) {
        int ar = A.length;
        int ac = A[0].length;
        int br = B.length;
        int bc = B[0].length;
        if (ac != br) {
            System.out.println("乘法矩阵A " + ar + " 行 " + ac + " 列 与 矩阵B " + br + " 行 " + bc + " 列 因维度不同。");
            return null;
        }
        double[][] result = new double[ar][bc];
        for (int i = 0; i < ar; i++) {
            for (int j = 0; j < bc; j++) {
                result[i][j] = result(A, B, i, j, ac);
            }
        }
        return result;
    }

    /**
     * 私有矩阵乘法
     *
     * @param A      矩阵A
     * @param B      矩阵B
     * @param row    矩阵行
     * @param column 矩阵列
     * @param key    矩阵相同元素个数（行/列长度）
     * @return 乘法矩阵结果
     */
    private double result(double[][] A, double[][] B, int row, int column, int key) {
        double result = 0;
        for (int i = 0; i < key; i++) {
            result += A[row][i] * B[i][column];
        }
        return result;
    }

    /**
     * 将一个数二值化
     *
     * @param A                    一个数
     * @param binarizationInterval 二值化（0到某个值的区间内）
     * @return 二值化的数
     */
    @Override
    public double binarization(double A, double binarizationInterval) {
        double result = 0;
        result = A / (binarizationInterval - 1);
        return result;
    }

    /**
     * 将向量二值化
     *
     * @param A                    向量A
     * @param binarizationInterval 二值化（0到某个值的区间内）
     * @return 二值化的向量
     */
    @Override
    public double[] binarization(double[] A, double binarizationInterval) {
        int ar = A.length;
        double[] result = new double[ar];
        for (int i = 0; i < ar; i++) {
            result[i] = A[i] / (binarizationInterval - 1);
        }
        return result;
    }

    /**
     * 将矩阵二值化
     *
     * @param A                    矩阵A
     * @param binarizationInterval 二值化（0到某个值的区间内）
     * @return 二值化的矩阵
     */
    @Override
    public double[][] binarization(double[][] A, double binarizationInterval) {
        int ar = A.length;
        int ac = A[0].length;
        double[][] result = new double[ar][ac];
        for (int i = 0; i < ar; i++) {
            for (int j = 0; j < ac; j++) {
                result[i][j] = A[i][j] / (binarizationInterval - 1);
            }
        }
        return result;
    }

    /**
     * @return 获取行数
     */
    public int getRow() {
        return row;
    }

    /**
     * 设置行数
     *
     * @param row 行
     */
    public void setRow(int row) {
        this.row = row;
    }

    /**
     * @return 获取列数
     */
    public int getColumn() {
        return column;
    }

    /**
     * 设置列
     *
     * @param column
     */
    public void setColumn(int column) {
        this.column = column;
    }

    /**
     * 打印向量
     *
     * @param vector 向量
     */
    public void printVector(double[] vector) {
        int vr = vector.length;
        for (int i = 0; i < vr; i++) {
            System.out.print(vector[i] + "  ");
        }
        System.out.println();
    }

    /**
     * 打印矩阵
     *
     * @param matrix 矩阵
     */
    public void printMatrix(double[][] matrix) {
        int mr = matrix.length;
        int mc = matrix[0].length;
        for (int i = 0; i < mr; i++) {
            for (int j = 0; j < mc; j++) {
                System.out.print(matrix[i][j] + "  ");
            }
            System.out.println();
        }
    }
}
